<!DOCTYPE html><html lang="zh-CN" data-theme="light"><head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width,initial-scale=1"><title>03-04数据库学习 | 个人博客</title><meta name="keywords" content="MySQL"><meta name="author" content="蔡哞哞"><meta name="copyright" content="蔡哞哞"><meta name="format-detection" content="telephone=no"><meta name="theme-color" content="#ffffff"><meta http-equiv="Cache-Control" content="no-transform"><meta http-equiv="Cache-Control" content="no-siteapp"><meta name="description" content="索引、事务、锁机制与InnoDB锁算法、大表优化、数据库字段设计规范、索引设计规范、数据库 SQL 开发规范、数据库操作行为规范">
<meta property="og:type" content="article">
<meta property="og:title" content="03-04数据库学习">
<meta property="og:url" content="https://caixm1025.gitee.io/2021/03/04/03-04%E6%95%B0%E6%8D%AE%E5%BA%93%E5%AD%A6%E4%B9%A0/index.html">
<meta property="og:site_name" content="个人博客">
<meta property="og:description" content="索引、事务、锁机制与InnoDB锁算法、大表优化、数据库字段设计规范、索引设计规范、数据库 SQL 开发规范、数据库操作行为规范">
<meta property="og:locale" content="zh_CN">
<meta property="og:image" content="https://caixm1025.gitee.io/img/%E5%9B%BE%E7%89%874.jpg">
<meta property="article:published_time" content="2021-03-04T15:38:11.000Z">
<meta property="article:modified_time" content="2021-03-04T15:41:42.990Z">
<meta property="article:author" content="蔡哞哞">
<meta property="article:tag" content="MySQL">
<meta name="twitter:card" content="summary">
<meta name="twitter:image" content="https://caixm1025.gitee.io/img/%E5%9B%BE%E7%89%874.jpg"><link rel="shortcut icon" href="/everyday/img/25.png"><link rel="canonical" href="https://caixm1025.gitee.io/2021/03/04/03-04%E6%95%B0%E6%8D%AE%E5%BA%93%E5%AD%A6%E4%B9%A0/"><link rel="preconnect" href="//cdn.jsdelivr.net"/><link rel="preconnect" href="//busuanzi.ibruce.info"/><link rel="stylesheet" href="/everyday/css/index.css"><link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fortawesome/fontawesome-free/css/all.min.css" media="print" onload="this.media='all'"><script>var GLOBAL_CONFIG = { 
  root: '/everyday/',
  algolia: undefined,
  localSearch: undefined,
  translate: undefined,
  noticeOutdate: undefined,
  highlight: {"plugin":"highlighjs","highlightCopy":true,"highlightLang":true},
  copy: {
    success: '复制成功',
    error: '复制错误',
    noSupport: '浏览器不支持'
  },
  relativeDate: {
    homepage: false,
    post: false
  },
  runtime: '',
  date_suffix: {
    just: '刚刚',
    min: '分钟前',
    hour: '小时前',
    day: '天前',
    month: '个月前'
  },
  copyright: undefined,
  lightbox: 'fancybox',
  Snackbar: undefined,
  source: {
    jQuery: 'https://cdn.jsdelivr.net/npm/jquery@latest/dist/jquery.min.js',
    justifiedGallery: {
      js: 'https://cdn.jsdelivr.net/npm/justifiedGallery/dist/js/jquery.justifiedGallery.min.js',
      css: 'https://cdn.jsdelivr.net/npm/justifiedGallery/dist/css/justifiedGallery.min.css'
    },
    fancybox: {
      js: 'https://cdn.jsdelivr.net/npm/@fancyapps/fancybox@latest/dist/jquery.fancybox.min.js',
      css: 'https://cdn.jsdelivr.net/npm/@fancyapps/fancybox@latest/dist/jquery.fancybox.min.css'
    }
  },
  isPhotoFigcaption: false,
  islazyload: false,
  isanchor: false
};

var saveToLocal = {
  set: function setWithExpiry(key, value, ttl) {
    const now = new Date()
    const expiryDay = ttl * 86400000
    const item = {
      value: value,
      expiry: now.getTime() + expiryDay,
    }
    localStorage.setItem(key, JSON.stringify(item))
  },

  get: function getWithExpiry(key) {
    const itemStr = localStorage.getItem(key)

    if (!itemStr) {
      return undefined
    }
    const item = JSON.parse(itemStr)
    const now = new Date()

    if (now.getTime() > item.expiry) {
      localStorage.removeItem(key)
      return undefined
    }
    return item.value
  }
}

// https://stackoverflow.com/questions/16839698/jquery-getscript-alternative-in-native-javascript
const getScript = url => new Promise((resolve, reject) => {
  const script = document.createElement('script')
  script.src = url
  script.async = true
  script.onerror = reject
  script.onload = script.onreadystatechange = function() {
    const loadState = this.readyState
    if (loadState && loadState !== 'loaded' && loadState !== 'complete') return
    script.onload = script.onreadystatechange = null
    resolve()
  }
  document.head.appendChild(script)
})</script><script id="config_change">var GLOBAL_CONFIG_SITE = { 
  isPost: true,
  isHome: false,
  isHighlightShrink: false,
  isToc: true,
  postUpdate: '2021-03-04 23:41:42'
}</script><noscript><style type="text/css">
  #nav {
    opacity: 1
  }
  .justified-gallery img {
    opacity: 1
  }

  #recent-posts time,
  #post-meta time {
    display: inline !important
  }
</style></noscript><script>(function () {  window.activateDarkMode = function () {
    document.documentElement.setAttribute('data-theme', 'dark')
    if (document.querySelector('meta[name="theme-color"]') !== null) {
      document.querySelector('meta[name="theme-color"]').setAttribute('content', '#0d0d0d')
    }
  }
  window.activateLightMode = function () {
    document.documentElement.setAttribute('data-theme', 'light')
   if (document.querySelector('meta[name="theme-color"]') !== null) {
      document.querySelector('meta[name="theme-color"]').setAttribute('content', '#ffffff')
    }
  }
  const autoChangeMode = 'false'
  const t = saveToLocal.get('theme')
  if (autoChangeMode === '1') {
    const isDarkMode = window.matchMedia('(prefers-color-scheme: dark)').matches
    const isLightMode = window.matchMedia('(prefers-color-scheme: light)').matches
    const isNotSpecified = window.matchMedia('(prefers-color-scheme: no-preference)').matches
    const hasNoSupport = !isDarkMode && !isLightMode && !isNotSpecified
    if (t === undefined) {
      if (isLightMode) activateLightMode()
      else if (isDarkMode) activateDarkMode()
      else if (isNotSpecified || hasNoSupport) {
        const now = new Date()
        const hour = now.getHours()
        const isNight = hour <= 6 || hour >= 18
        isNight ? activateDarkMode() : activateLightMode()
      }
      window.matchMedia('(prefers-color-scheme: dark)').addListener(function (e) {
        if (saveToLocal.get('theme') === undefined) {
          e.matches ? activateDarkMode() : activateLightMode()
        }
      })
    } else if (t === 'light') activateLightMode()
    else activateDarkMode()
  } else if (autoChangeMode === '2') {
    const now = new Date()
    const hour = now.getHours()
    const isNight = hour <= 6 || hour >= 18
    if (t === undefined) isNight ? activateDarkMode() : activateLightMode()
    else if (t === 'light') activateLightMode()
    else activateDarkMode()
  } else {
    if (t === 'dark') activateDarkMode()
    else if (t === 'light') activateLightMode()
  }const asideStatus = saveToLocal.get('aside-status')
if (asideStatus !== undefined) {
   if (asideStatus === 'hide') {
     document.documentElement.classList.add('hide-aside')
   } else {
     document.documentElement.classList.remove('hide-aside')
   }
}})()</script><meta name="generator" content="Hexo 5.3.0"><link rel="alternate" href="/everyday/atom.xml" title="个人博客" type="application/atom+xml">
</head><body><div id="sidebar"><div id="menu-mask"></div><div id="sidebar-menus"><div class="author-avatar"><img class="avatar-img" src="/everyday/img/image011.png" onerror="onerror=null;src='/img/friend_404.gif'" alt="avatar"/></div><div class="site-data"><div class="data-item is-center"><div class="data-item-link"><a href="/everyday/archives/"><div class="headline">文章</div><div class="length-num">28</div></a></div></div><div class="data-item is-center"><div class="data-item-link"><a href="/everyday/tags/"><div class="headline">标签</div><div class="length-num">19</div></a></div></div><div class="data-item is-center"><div class="data-item-link"><a href="/everyday/categories/"><div class="headline">分类</div><div class="length-num">4</div></a></div></div></div><hr/><div class="menus_items"><div class="menus_item"><a class="site-page" href="/everyday/"><i class="fa-fw fas fa-home"></i><span> 首页</span></a></div><div class="menus_item"><a class="site-page" href="/everyday/archives/"><i class="fa-fw fas fa-archive"></i><span> 归档</span></a></div><div class="menus_item"><a class="site-page" href="/everyday/tags/"><i class="fa-fw fas fa-tags"></i><span> 标签</span></a></div><div class="menus_item"><a class="site-page" href="/everyday/categories/"><i class="fa-fw fas fa-folder-open"></i><span> 分类</span></a></div><div class="menus_item"><a class="site-page" href="javascript:void(0);"><i class="fa-fw fas fa-list"></i><span> 列表</span><i class="fas fa-chevron-down expand"></i></a><ul class="menus_item_child"><li><a class="site-page" href="/everyday/music/"><i class="fa-fw fas fa-music"></i><span> 音乐</span></a></li><li><a class="site-page" href="/everyday/movies/"><i class="fa-fw fas fa-video"></i><span> 电影</span></a></li></ul></div><div class="menus_item"><a class="site-page" href="/everyday/link/"><i class="fa-fw fas fa-link"></i><span> 友情链接</span></a></div><div class="menus_item"><a class="site-page" href="/everyday/about/"><i class="fa-fw fas fa-heart"></i><span> 关于</span></a></div></div></div></div><div class="post" id="body-wrap"><header class="post-bg" id="page-header" style="background-image: url(/everyday/img/%E5%9B%BE%E7%89%874.jpg)"><nav id="nav"><span id="blog_name"><a id="site-name" href="/everyday/">个人博客</a></span><div id="menus"><div class="menus_items"><div class="menus_item"><a class="site-page" href="/everyday/"><i class="fa-fw fas fa-home"></i><span> 首页</span></a></div><div class="menus_item"><a class="site-page" href="/everyday/archives/"><i class="fa-fw fas fa-archive"></i><span> 归档</span></a></div><div class="menus_item"><a class="site-page" href="/everyday/tags/"><i class="fa-fw fas fa-tags"></i><span> 标签</span></a></div><div class="menus_item"><a class="site-page" href="/everyday/categories/"><i class="fa-fw fas fa-folder-open"></i><span> 分类</span></a></div><div class="menus_item"><a class="site-page" href="javascript:void(0);"><i class="fa-fw fas fa-list"></i><span> 列表</span><i class="fas fa-chevron-down expand"></i></a><ul class="menus_item_child"><li><a class="site-page" href="/everyday/music/"><i class="fa-fw fas fa-music"></i><span> 音乐</span></a></li><li><a class="site-page" href="/everyday/movies/"><i class="fa-fw fas fa-video"></i><span> 电影</span></a></li></ul></div><div class="menus_item"><a class="site-page" href="/everyday/link/"><i class="fa-fw fas fa-link"></i><span> 友情链接</span></a></div><div class="menus_item"><a class="site-page" href="/everyday/about/"><i class="fa-fw fas fa-heart"></i><span> 关于</span></a></div></div><div id="toggle-menu"><a class="site-page"><i class="fas fa-bars fa-fw"></i></a></div></div></nav><div id="post-info"><h1 class="post-title">03-04数据库学习</h1><div id="post-meta"><div class="meta-firstline"><span class="post-meta-date"><i class="far fa-calendar-alt fa-fw post-meta-icon"></i><span class="post-meta-label">发表于</span><time class="post-meta-date-created" datetime="2021-03-04T15:38:11.000Z" title="发表于 2021-03-04 23:38:11">2021-03-04</time><span class="post-meta-separator">|</span><i class="fas fa-history fa-fw post-meta-icon"></i><span class="post-meta-label">更新于</span><time class="post-meta-date-updated" datetime="2021-03-04T15:41:42.990Z" title="更新于 2021-03-04 23:41:42">2021-03-04</time></span><span class="post-meta-categories"><span class="post-meta-separator">|</span><i class="fas fa-inbox fa-fw post-meta-icon"></i><a class="post-meta-categories" href="/everyday/categories/%E6%95%B0%E6%8D%AE%E5%BA%93/">数据库</a></span></div><div class="meta-secondline"><span class="post-meta-separator">|</span><span class="post-meta-pv-cv"><i class="far fa-eye fa-fw post-meta-icon"></i><span class="post-meta-label">阅读量:</span><span id="busuanzi_value_page_pv"></span></span></div></div></div></header><main class="layout" id="content-inner"><div id="post"><article class="post-content" id="article-container"><h1 id="全文绝大部分出处：https-gitee-com-SnailClimb-JavaGuide"><a href="#全文绝大部分出处：https-gitee-com-SnailClimb-JavaGuide" class="headerlink" title="全文绝大部分出处：https://gitee.com/SnailClimb/JavaGuide"></a>全文绝大部分出处：<a target="_blank" rel="noopener" href="https://gitee.com/SnailClimb/JavaGuide">https://gitee.com/SnailClimb/JavaGuide</a></h1><h2 id="1、索引"><a href="#1、索引" class="headerlink" title="1、索引"></a>1、索引</h2><p>MySQL的BTree索引使用的是B树中的B+Tree，但对于主要的两种存储引擎的实现方式是不同的。</p>
<p><strong>MyISAM:</strong> B+Tree叶节点的data域存放的是数据记录的地址。在索引检索的时候，首先按照B+Tree搜索算法搜索索引，如果指定的Key存在，则取出其 data 域的值，然后以 data 域的值为地址读取相应的数据记录。这被称为“非聚簇索引”。</p>
<p><strong>InnoDB:</strong> 其数据文件本身就是索引文件。相比MyISAM，索引文件和数据文件是分离的，其表数据文件本身就是按B+Tree组织的一个索引结构，树的叶节点data域保存了完整的数据记录。这个索引的key是数据表的主键，因此InnoDB表数据文件本身就是主索引。这被称为“聚簇索引（或聚集索引）”。而其余的索引都作为辅助索引，辅助索引的data域存储相应记录<strong>主键的值</strong>而不是地址，这也是和MyISAM不同的地方。<strong>在根据主索引搜索时，直接找到key所在的节点即可取出数据；在根据辅助索引查找时，则需要先取出主键的值，再走一遍主索引。</strong> <strong>因此，在设计表的时候，不建议使用过长的字段作为主键，也不建议使用非单调的字段作为主键，这样会造成主索引频繁分裂。</strong></p>
<hr>
<h2 id="2、查询缓存的使用"><a href="#2、查询缓存的使用" class="headerlink" title="2、查询缓存的使用"></a>2、查询缓存的使用</h2><p>缓存建立之后，MySQL的查询缓存系统会跟踪查询中涉及的每张表，如果这些表（数据或结构）发生变化，那么和这张表相关的所有缓存数据都将失效。</p>
<p><strong>缓存虽然能够提升数据库的查询性能，但是缓存同时也带来了额外的开销，每次查询后都要做一次缓存操作，失效后还要销毁。</strong> 因此，开启查询缓存要谨慎，尤其对于写密集的应用来说更是如此。如果开启，要注意合理控制缓存空间大小，一般来说其大小设置为几十MB比较合适。此外，<strong>还可以通过sql_cache和sql_no_cache来控制某个查询语句是否需要缓存：</strong></p>
<figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">select</span> sql_no_cache <span class="built_in">count</span>(<span class="operator">*</span>) <span class="keyword">from</span> usr;</span><br></pre></td></tr></table></figure>
<hr>
<h2 id="3、事务"><a href="#3、事务" class="headerlink" title="3、事务"></a>3、事务</h2><p><strong>事务是逻辑上的一组操作，要么都执行，要么都不执行。</strong></p>
<h4 id="事务的四大特性-ACID"><a href="#事务的四大特性-ACID" class="headerlink" title="事务的四大特性(ACID)"></a>事务的四大特性(ACID)</h4><ol>
<li><strong>原子性（Atomicity）：</strong> 事务是最小的执行单位，不允许分割。事务的原子性确保动作要么全部完成，要么完全不起作用；</li>
<li><strong>一致性（Consistency）：</strong> 执行事务后，<strong>数据库从一个正确的状态变化到另一个正确的状态</strong>；</li>
<li><strong>隔离性（Isolation）：</strong> 并发访问数据库时，一个用户的事务不被其他事务所干扰，各并发事务之间数据库是独立的；</li>
<li><strong>持久性（Durability）：</strong> 一个事务被提交之后。它对数据库中数据的改变是持久的，即使数据库发生故障也不应该对其有任何影响。</li>
</ol>
<h4 id="并发事务带来哪些问题"><a href="#并发事务带来哪些问题" class="headerlink" title="并发事务带来哪些问题?"></a>并发事务带来哪些问题?</h4><p>在典型的应用程序中，多个事务并发运行，经常会操作相同的数据来完成各自的任务（多个用户对同一数据进行操作）。并发虽然是必须的，但可能会导致以下的问题。</p>
<ul>
<li><strong>脏读（Dirty read）:</strong> 当一个事务正在访问数据并且对数据进行了修改，而这种修改还没有提交到数据库中，这时另外一个事务也访问了这个数据，然后使用了这个数据。因为这个数据是还没有提交的数据，那么另外一个事务读到的这个数据是“脏数据”，依据“脏数据”所做的操作可能是不正确的。</li>
<li><strong>丢失修改（Lost to modify）:</strong> 指在一个事务读取一个数据时，另外一个事务也访问了该数据，那么在第一个事务中修改了这个数据后，第二个事务也修改了这个数据。这样第一个事务内的修改结果就被丢失，因此称为丢失修改。    例如：事务1读取某表中的数据A=20，事务2也读取A=20，事务1修改A=A-1，事务2也修改A=A-1，最终结果A=19，事务1的修改被丢失。</li>
<li><strong>不可重复读（Unrepeatableread）:</strong> 指在一个事务内多次读同一数据。在这个事务还没有结束时，另一个事务也访问该数据。那么，在第一个事务中的两次读数据之间，由于第二个事务的修改导致第一个事务两次读取的数据可能不太一样。这就发生了在一个事务内两次读到的数据是不一样的情况，因此称为不可重复读。</li>
<li><strong>幻读（Phantom read）:</strong> 幻读与不可重复读类似。它发生在一个事务（T1）读取了几行数据，接着另一个并发事务（T2）插入了一些数据时。在随后的查询中，第一个事务（T1）就会发现多了一些原本不存在的记录，就好像发生了幻觉一样，所以称为幻读。</li>
</ul>
<p><strong>不可重复读和幻读区别：</strong></p>
<p>不可重复读的重点是<strong>修改</strong>比如多次读取一条记录发现其中某些列的值被修改，幻读的重点在于<strong>新增或者删除</strong>比如多次读取一条记录发现记录增多或减少了。</p>
<h4 id="事务隔离级别有哪些-MySQL的默认隔离级别是"><a href="#事务隔离级别有哪些-MySQL的默认隔离级别是" class="headerlink" title="事务隔离级别有哪些?MySQL的默认隔离级别是?"></a>事务隔离级别有哪些?MySQL的默认隔离级别是?</h4><p><strong>SQL 标准定义了四个隔离级别：</strong></p>
<ul>
<li><strong>READ-UNCOMMITTED(读取未提交)：</strong> 最低的隔离级别，允许读取尚未提交的数据变更，<strong>可能会导致脏读、幻读或不可重复读</strong>。</li>
<li><strong>READ-COMMITTED(读取已提交)：</strong> 允许读取并发事务已经提交的数据，<strong>可以阻止脏读，但是幻读或不可重复读仍有可能发生</strong>。</li>
<li><strong>REPEATABLE-READ(可重复读)：</strong>  对同一字段的多次读取结果都是一致的，除非数据是被本身事务自己所修改，<strong>可以阻止脏读和不可重复读，但幻读仍有可能发生</strong>。</li>
<li><strong>SERIALIZABLE(可串行化)：</strong> 最高的隔离级别，完全服从ACID的隔离级别。所有的事务依次逐个执行，这样事务之间就完全不可能产生干扰，也就是说，<strong>该级别可以防止脏读、不可重复读以及幻读</strong>。</li>
</ul>
<hr>
<table>
<thead>
<tr>
<th align="center">隔离级别</th>
<th align="center">脏读</th>
<th align="center">不可重复读</th>
<th align="center">幻影读</th>
</tr>
</thead>
<tbody><tr>
<td align="center">READ-UNCOMMITTED</td>
<td align="center">√</td>
<td align="center">√</td>
<td align="center">√</td>
</tr>
<tr>
<td align="center">READ-COMMITTED</td>
<td align="center">×</td>
<td align="center">√</td>
<td align="center">√</td>
</tr>
<tr>
<td align="center">REPEATABLE-READ</td>
<td align="center">×</td>
<td align="center">×</td>
<td align="center">√</td>
</tr>
<tr>
<td align="center">SERIALIZABLE</td>
<td align="center">×</td>
<td align="center">×</td>
<td align="center">×</td>
</tr>
</tbody></table>
<p>MySQL InnoDB 存储引擎的默认支持的隔离级别是 <strong>REPEATABLE-READ（可重读）</strong>。我们可以通过<code>SELECT @@tx_isolation;</code>命令来查看，MySQL 8.0 该命令改为<code>SELECT @@transaction_isolation;</code></p>
<figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">mysql<span class="operator">&gt;</span> <span class="keyword">SELECT</span> @<span class="variable">@tx</span>_isolation;</span><br><span class="line"><span class="operator">+</span><span class="comment">-----------------+</span></span><br><span class="line"><span class="operator">|</span> @<span class="variable">@tx</span>_isolation  <span class="operator">|</span></span><br><span class="line"><span class="operator">+</span><span class="comment">-----------------+</span></span><br><span class="line"><span class="operator">|</span> REPEATABLE<span class="operator">-</span>READ <span class="operator">|</span></span><br><span class="line"><span class="operator">+</span><span class="comment">-----------------+</span></span><br></pre></td></tr></table></figure>
<p>这里需要注意的是：与 SQL 标准不同的地方在于 InnoDB 存储引擎在 <strong>REPEATABLE-READ（可重读）</strong><br>事务隔离级别下使用的是<strong>Next-Key Lock</strong> 锁算法，因此可以避免幻读的产生，这与其他数据库系统(如 SQL Server)<br>是不同的。所以说InnoDB 存储引擎的默认支持的隔离级别是 <strong>REPEATABLE-READ（可重读）</strong> 已经可以完全保证事务的隔离性要求，即达到了<br> SQL标准的 <strong>SERIALIZABLE(可串行化)</strong> 隔离级别。因为隔离级别越低，事务请求的锁越少，所以大部分数据库系统的隔离级别都是 <strong>READ-COMMITTED(读取提交内容)</strong> ，但是你要知道的是InnoDB 存储引擎默认使用 <strong>REPEAaTABLE-READ（可重读）</strong> 并不会有任何性能损失。</p>
<p>InnoDB 存储引擎在 <strong>分布式事务</strong> 的情况下一般会用到 <strong>SERIALIZABLE(可串行化)</strong> 隔离级别。</p>
<hr>
<h2 id="4、锁机制与InnoDB锁算法"><a href="#4、锁机制与InnoDB锁算法" class="headerlink" title="4、锁机制与InnoDB锁算法"></a>4、锁机制与InnoDB锁算法</h2><p><strong>MyISAM和InnoDB存储引擎使用的锁：</strong></p>
<ul>
<li>MyISAM采用表级锁(table-level locking)。</li>
<li>InnoDB支持行级锁(row-level locking)和表级锁,默认为行级锁</li>
</ul>
<p><strong>表级锁和行级锁对比：</strong></p>
<ul>
<li><strong>表级锁：</strong> MySQL中锁定 <strong>粒度最大</strong> 的一种锁，对当前操作的整张表加锁，实现简单，资源消耗也比较少，加锁快，不会出现死锁。其锁定粒度最大，触发锁冲突的概率最高，并发度最低，MyISAM和 InnoDB引擎都支持表级锁。</li>
<li><strong>行级锁：</strong> MySQL中锁定 <strong>粒度最小</strong> 的一种锁，只针对当前操作的行进行加锁。 行级锁能大大减少数据库操作的冲突。其加锁粒度最小，并发度高，但加锁的开销也最大，加锁慢，会出现死锁。 </li>
</ul>
<p><strong>InnoDB存储引擎的锁的算法有三种：</strong></p>
<ul>
<li>Record lock：单个行记录上的锁</li>
<li>Gap lock：间隙锁，锁定一个范围，不包括记录本身</li>
<li>Next-key lock：record+gap 锁定一个范围，包含记录本身</li>
</ul>
<p><strong>相关知识点：</strong></p>
<ol>
<li>innodb对于行的查询使用next-key lock</li>
<li>Next-locking keying为了解决Phantom Problem幻读问题</li>
<li>当查询的索引含有唯一属性时，将next-key lock降级为record key</li>
<li>Gap锁设计的目的是为了阻止多个事务将记录插入到同一范围内，而这会导致幻读问题的产生</li>
<li>有两种方式显式关闭gap锁：（除了外键约束和唯一性检查外，其余情况仅使用record lock） A. 将事务隔离级别设置为RC B. 将参数innodb_locks_unsafe_for_binlog设置为1</li>
</ol>
<hr>
<h2 id="5、大表优化"><a href="#5、大表优化" class="headerlink" title="5、大表优化"></a>5、大表优化</h2><h4 id="1-限定数据的范围"><a href="#1-限定数据的范围" class="headerlink" title="1. 限定数据的范围"></a>1. 限定数据的范围</h4><p>务必禁止不带任何限制数据范围条件的查询语句。比如：我们当用户在查询订单历史的时候，我们可以控制在一个月的范围内；</p>
<h4 id="2-读-写分离"><a href="#2-读-写分离" class="headerlink" title="2. 读/写分离"></a>2. 读/写分离</h4><p>经典的数据库拆分方案，主库负责写，从库负责读；</p>
<h4 id="3-垂直分区"><a href="#3-垂直分区" class="headerlink" title="3. 垂直分区"></a>3. 垂直分区</h4><p> <strong>根据数据库里面数据表的相关性进行拆分。</strong> 例如，用户表中既有用户的登录信息又有用户的基本信息，可以将用户表拆分成两个单独的表，甚至放到单独的库做分库。</p>
<p> <strong>简单来说垂直拆分是指数据表列的拆分，把一张列比较多的表拆分为多张表。</strong></p>
<ul>
<li><strong>垂直拆分的优点：</strong> 可以使得列数据变小，在查询时减少读取的Block数，减少I/O次数。此外，垂直分区可以简化表的结构，易于维护。</li>
<li><strong>垂直拆分的缺点：</strong> <strong>主键会出现冗余，需要管理冗余列，并会引起Join操作</strong>，可以通过在应用层进行Join来解决。此外，垂直分区会让事务变得更加复杂；</li>
</ul>
<h4 id="4-水平分区"><a href="#4-水平分区" class="headerlink" title="4. 水平分区"></a>4. 水平分区</h4><p><strong>保持数据表结构不变，通过某种策略存储数据分片。这样每一片数据分散到不同的表或者库中，达到了分布式的目的。 水平拆分可以支撑非常大的数据量。</strong> </p>
<p>水平拆分可以支持非常大的数据量。需要注意的一点是：分表仅仅是解决了单一表数据过大的问题，但由于表的数据还是在同一台机器上，其实对于提升MySQL并发能力没有什么意义，所以 <strong>水平拆分最好分库</strong> 。<strong>尽量不要对数据进行分片，因为拆分会带来逻辑、部署、运维的各种复杂度</strong> ，一般的数据表在优化得当的情况下支撑千万以下的数据量是没有太大问题的。如果实在要分片，尽量选择<em>客户端分片架构</em>，这样可以减少一次和中间件的网络I/O。</p>
<p>补充一下数据库分片的两种常见方案：</p>
<ul>
<li><strong>客户端代理：</strong>  <strong>分片逻辑在应用端，封装在jar包中，通过修改或者封装JDBC层来实现。</strong> 当当网的 <strong>Sharding-JDBC</strong> 、阿里的TDDL是两种比较常用的实现。</li>
<li><strong>中间件代理：</strong> <strong>在应用和数据中间加了一个代理层。分片逻辑统一维护在中间件服务中。</strong> 我们现在谈的 <strong>Mycat</strong> 、360的Atlas、网易的DDB等等都是这种架构的实现。</li>
</ul>
<p>详细内容可以参考： MySQL大表优化方案: [<a target="_blank" rel="noopener" href="https://segmentfault.com/a/1190000006158186]">https://segmentfault.com/a/1190000006158186]</a>(</p>
<h3 id="解释一下什么是池化设计思想。什么是数据库连接池-为什么需要数据库连接池"><a href="#解释一下什么是池化设计思想。什么是数据库连接池-为什么需要数据库连接池" class="headerlink" title="解释一下什么是池化设计思想。什么是数据库连接池?为什么需要数据库连接池?"></a>解释一下什么是池化设计思想。什么是数据库连接池?为什么需要数据库连接池?</h3><p>池化设计应该不是一个新名词。我们常见的如java线程池、jdbc连接池、redis连接池等就是这类设计的代表实现。这种设计会初始预设资源，解决的问题就是抵消每次获取资源的消耗，如创建线程的开销，获取远程连接的开销等。就好比你去食堂打饭，打饭的大妈会先把饭盛好几份放那里，你来了就直接拿着饭盒加菜即可，不用再临时又盛饭又打菜，效率就高了。除了初始化资源，池化设计还包括如下这些特征：池子的初始值、池子的活跃值、池子的最大值等，这些特征可以直接映射到java线程池和数据库连接池的成员属性中。这篇文章对<a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s?__biz=Mzg2OTA0Njk0OA==&mid=2247485679&idx=1&sn=57dbca8c9ad49e1f3968ecff04a4f735&chksm=cea24724f9d5ce3212292fac291234a760c99c0960b5430d714269efe33554730b5f71208582&token=1141994790&lang=zh_CN#rd">池化设计思想</a>介绍的还不错，直接复制过来，避免重复造轮子了。</p>
<p>数据库连接本质就是一个 socket 的连接。数据库服务端还要维护一些缓存和用户权限信息之类的 所以占用了一些内存。我们可以把数据库连接池是看做是维护的数据库连接的缓存，以便将来需要对数据库的请求时可以重用这些连接。为每个用户打开和维护数据库连接，尤其是对动态数据库驱动的网站应用程序的请求，既昂贵又浪费资源。<strong>在连接池中，创建连接后，将其放置在池中，并再次使用它，因此不必建立新的连接。如果使用了所有连接，则会建立一个新连接并将其添加到池中</strong>。 连接池还减少了用户必须等待建立与数据库的连接的时间。</p>
<h3 id="分库分表之后-id-主键如何处理？"><a href="#分库分表之后-id-主键如何处理？" class="headerlink" title="分库分表之后,id 主键如何处理？"></a>分库分表之后,id 主键如何处理？</h3><p>因为要是分成多个表之后，每个表都是从 1 开始累加，这样是不对的，我们需要一个全局唯一的 id 来支持。</p>
<p>生成全局 id 有下面这几种方式：</p>
<ul>
<li><strong>UUID</strong>：不适合作为主键，因为太长了，并且无序不可读，查询效率低。比较适合用于生成唯一的名字的标示比如文件的名字。</li>
<li><strong>数据库自增 id</strong> : 两台数据库分别设置不同步长，生成不重复ID的策略来实现高可用。这种方式生成的 id 有序，但是需要独立部署数据库实例，成本高，还会有性能瓶颈。</li>
<li><strong>利用 redis 生成 id :</strong> 性能比较好，灵活方便，不依赖于数据库。但是，引入了新的组件造成系统更加复杂，可用性降低，编码更加复杂，增加了系统成本。</li>
<li><strong>Twitter的snowflake算法</strong> ：Github 地址：<a target="_blank" rel="noopener" href="https://github.com/twitter-archive/snowflake%E3%80%82">https://github.com/twitter-archive/snowflake。</a></li>
<li><strong>美团的<a target="_blank" rel="noopener" href="https://tech.meituan.com/2017/04/21/mt-leaf.html">Leaf</a>分布式ID生成系统</strong> ：Leaf</li>
<li> 是美团开源的分布式ID生成器，能保证全局唯一性、趋势递增、单调递增、信息安全，里面也提到了几种分布式方案的对比，但也需要依赖关系数据库、Zookeeper等中间件。感觉还不错。美团技术团队的一篇文章：<a target="_blank" rel="noopener" href="https://tech.meituan.com/2017/04/21/mt-leaf.html">https://tech.meituan.com/2017/04/21/mt-leaf.html</a> 。</li>
</ul>
<h4 id="尽量做到冷热数据分离-减小表的宽度"><a href="#尽量做到冷热数据分离-减小表的宽度" class="headerlink" title="尽量做到冷热数据分离,减小表的宽度"></a>尽量做到冷热数据分离,减小表的宽度</h4><blockquote>
<p>MySQL 限制每个表最多存储 4096 列，并且每一行数据的大小不能超过 65535 字节。</p>
</blockquote>
<p>减少磁盘 IO,保证热数据的内存缓存命中率（表越宽，把表装载进内存缓冲池时所占用的内存也就越大,也会消耗更多的 IO）；</p>
<p>更有效的利用缓存，避免读入无用的冷数据；</p>
<p>经常一起使用的列放到一个表中（避免更多的关联操作）。</p>
<h4 id="禁止在表中建立预留字段"><a href="#禁止在表中建立预留字段" class="headerlink" title="禁止在表中建立预留字段"></a>禁止在表中建立预留字段</h4><p>预留字段的命名很难做到见名识义。</p>
<p>预留字段无法确认存储的数据类型，所以无法选择合适的类型。</p>
<p>对预留字段类型的修改，会对表进行锁定。</p>
<h4 id="禁止在数据库中存储图片-文件等大的二进制数据"><a href="#禁止在数据库中存储图片-文件等大的二进制数据" class="headerlink" title="禁止在数据库中存储图片,文件等大的二进制数据"></a>禁止在数据库中存储图片,文件等大的二进制数据</h4><p>通常文件很大，会短时间内造成数据量快速增长，数据库进行数据库读取时，通常会进行大量的随机 IO 操作，文件很大时，IO 操作很耗时。</p>
<p>通常存储于文件服务器，数据库只存储文件地址信息</p>
<h4 id="禁止在线上做数据库压力测试"><a href="#禁止在线上做数据库压力测试" class="headerlink" title="禁止在线上做数据库压力测试"></a>禁止在线上做数据库压力测试</h4><hr>
<h2 id="6、数据库字段设计规范"><a href="#6、数据库字段设计规范" class="headerlink" title="6、数据库字段设计规范"></a>6、数据库字段设计规范</h2><h4 id="优先选择符合存储需要的最小的数据类型"><a href="#优先选择符合存储需要的最小的数据类型" class="headerlink" title="优先选择符合存储需要的最小的数据类型"></a>优先选择符合存储需要的最小的数据类型</h4><p>列的字段越大，建立索引时所需要的空间也就越大，这样一页中所能存储的索引节点的数量也就越少也越少，在遍历时所需要的 IO 次数也就越多，索引的性能也就越差。</p>
<p><strong>将字符串转换成数字类型存储,如:将 IP 地址转换成整形数据</strong></p>
<p>MySQL 提供了两个方法来处理 ip 地址</p>
<ul>
<li>inet_aton 把 ip 转为无符号整型 (4-8 位)</li>
<li>inet_ntoa 把整型的 ip 转为地址</li>
</ul>
<p>插入数据前，先用 inet_aton 把 ip 地址转为整型，可以节省空间，显示数据时，使用 inet_ntoa 把整型的 ip 地址转为地址显示即可。</p>
<p><strong>对于非负型的数据 (如自增 ID,整型 IP) 来说,要优先使用无符号整型来存储</strong></p>
<p>无符号相对于有符号可以多出一倍的存储空间</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">SIGNED INT -2147483648~2147483647</span><br><span class="line">UNSIGNED INT 0~4294967295</span><br></pre></td></tr></table></figure>
<p>VARCHAR(N) 中的 N 代表的是字符数，而不是字节数，使用 UTF8 存储 255 个汉字 Varchar(255)=765 个字节。<strong>过大的长度会消耗更多的内存。</strong></p>
<h4 id="避免使用-TEXT-BLOB-数据类型，最常见的-TEXT-类型可以存储-64k-的数据"><a href="#避免使用-TEXT-BLOB-数据类型，最常见的-TEXT-类型可以存储-64k-的数据" class="headerlink" title="避免使用 TEXT,BLOB 数据类型，最常见的 TEXT 类型可以存储 64k 的数据"></a>避免使用 TEXT,BLOB 数据类型，最常见的 TEXT 类型可以存储 64k 的数据</h4><p><strong>建议把 BLOB 或是 TEXT 列分离到单独的扩展表中</strong></p>
<p>MySQL 内存临时表不支持 TEXT、BLOB 这样的大数据类型，如果查询中包含这样的数据，在排序等操作时，就不能使用内存临时表，必须使用磁盘临时表进行。而且对于这种数据，MySQL 还是要进行二次查询，会使 sql 性能变得很差，但是不是说一定不能使用这样的数据类型。</p>
<p>如果一定要使用，建议把 BLOB 或是 TEXT 列分离到单独的扩展表中，*<em>查询时一定不要使用 select * 而只需要取出必要的列，不需要 TEXT 列的数据时不要对该列进行查询*</em>。</p>
<p><strong>TEXT 或 BLOB 类型只能使用前缀索引</strong></p>
<p>因为<a target="_blank" rel="noopener" href="http://mp.weixin.qq.com/s?__biz=MzI4Njc5NjM1NQ==&mid=2247487885&idx=1&sn=65b1bf5f7d4505502620179669a9c2df&chksm=ebd62ea1dca1a7b7bf884bcd9d538d78ba064ee03c09436ca8e57873b1d98a55afd6d7884cfc&scene=21#wechat_redirect">MySQL</a> 对索引字段长度是有限制的，所以 TEXT 类型只能使用前缀索引，并且 TEXT 列上是不能有默认值的</p>
<p><strong>避免使用 ENUM 类型</strong></p>
<p>修改 ENUM 值需要使用 ALTER 语句</p>
<p>ENUM 类型的 ORDER BY 操作效率低，需要额外操作</p>
<p>禁止使用数值作为 ENUM 的枚举值</p>
<p><strong>尽可能把所有列定义为 NOT NULL</strong></p>
<p><strong>原因：</strong></p>
<p>索引 NULL 列需要额外的空间来保存，所以要占用更多的空间</p>
<p>进行比较和计算时要对 NULL 值做特别的处理</p>
<p><strong>使用 TIMESTAMP(4 个字节) 或 DATETIME 类型 (8 个字节) 存储时间</strong></p>
<p>TIMESTAMP 存储的时间范围 1970-01-01 00:00:01 ~ 2038-01-19-03:14:07</p>
<p>TIMESTAMP 占用 <strong>4 字节</strong>和 INT 相同，但比 INT 可读性高</p>
<p>超出 TIMESTAMP 取值范围的使用 <em>DATETIME</em> 类型存储</p>
<p><strong>经常会有人用字符串存储日期型的数据（不正确的做法）</strong></p>
<ul>
<li>缺点 1：无法用日期函数进行计算和比较</li>
<li>缺点 2：用字符串存储日期要占用更多的空间</li>
</ul>
<p> <strong>同财务相关的金额类数据必须使用 decimal 类型</strong></p>
<ul>
<li><strong>非精准浮点</strong>：float,double</li>
<li><strong>精准浮点</strong>：decimal</li>
</ul>
<p>Decimal 类型为精准浮点数，在计算时不会丢失精度</p>
<p>占用空间由定义的宽度决定，每 4 个字节可以存储 9 位数字，并且<strong>小数点要占用一个字节</strong></p>
<p>可用于存储比 bigint 更大的整型数据</p>
<hr>
<h2 id="7、索引设计规范"><a href="#7、索引设计规范" class="headerlink" title="7、索引设计规范"></a>7、索引设计规范</h2><p><strong>限制每张表上的索引数量,建议单张表索引不超过 5 个</strong></p>
<p>索引可以增加查询效率，但同样也会降低插入和更新的效率，甚至有些情况下会降低查询效率。</p>
<p>因为 MySQL 优化器在选择如何优化查询时，会根据统一信息，对每一个可以用到的索引来进行评估，以生成出一个最好的执行计划，如果同时有很多个索引都可以用于查询，就会增加 MySQL 优化器生成执行计划的时间，同样会降低查询性能。</p>
<p><strong>禁止给表中的每一列都建立单独的索引</strong></p>
<p><strong>每个 Innodb 表必须有个主键</strong></p>
<p>Innodb 是一种索引组织表：数据的存储的逻辑顺序和索引的顺序是相同的。每个表都可以有多个索引，但是表的存储顺序只能有一种。</p>
<p>Innodb 是按照主键索引的顺序来组织表的</p>
<ul>
<li>不要使用更新频繁的列作为主键，不适用多列主键（相当于联合索引）</li>
<li>不要使用 UUID,MD5,HASH,字符串列作为主键（<strong>无法保证数据的顺序增长</strong>）</li>
<li><strong>主键建议使用自增 ID 值</strong></li>
</ul>
<p><strong>常见索引列建议</strong></p>
<ul>
<li>出现在 SELECT、UPDATE、DELETE 语句的 WHERE 从句中的列</li>
<li>包含在 ORDER BY、GROUP BY、DISTINCT 中的字段</li>
<li>并不要将符合 1 和 2 中的字段的列都建立一个索引， 通常将 1、2 中的字段<strong>建立联合索引</strong>效果更好</li>
<li>多表 join 的关联列</li>
</ul>
<p><strong>如何选择索引列的顺序</strong></p>
<p>建立索引的目的是：希望通过索引进行数据查找，减少随机 IO，增加查询性能 ，索引能过滤出越少的数据，则从磁盘中读入的数据也就越少。</p>
<ul>
<li><strong>区分度最高</strong>的放在<strong>联合索引的最左侧</strong>（区分度=列中不同值的数量/列的总行数）</li>
<li>尽量把<strong>字段长度小</strong>的列放在联合索引的最左侧（因为字段长度越小，一页能存储的数据量越大，IO 性能也就越好）</li>
<li><strong>使用最频繁的列</strong>放到联合索引的左侧（这样可以比较少的建立一些索引）</li>
</ul>
<p> <strong>避免建立冗余索引和重复索引</strong></p>
<ul>
<li>重复索引示例：primary key(id)、index(id)、unique index(id)</li>
<li>冗余索引示例：index(a,b,c)、index(a,b)、index(a)</li>
</ul>
<p><strong>对于频繁的查询优先考虑使用覆盖索引</strong></p>
<blockquote>
<p>覆盖索引：就是包含了所有查询字段 (where,select,ordery by,group by 包含的字段) 的索引</p>
</blockquote>
<ul>
<li><strong>避免 Innodb 表进行索引的二次查询:</strong> Innodb 是以聚集索引的顺序来存储的，对于 Innodb 来说，二级索引在叶子节点中所保存的是行的主键信息，如果是用二级索引查询数据的话，在查找到相应的键值后，还要通过主键进行二次查询才能获取我们真实所需要的数据。而在覆盖索引中，二级索引的键值中可以获取所有的数据，避免了对主键的二次查询 ，减少了 IO 操作，提升了查询效率。</li>
<li><strong>可以把随机 IO 变成顺序 IO 加快查询效率:</strong> 由于覆盖索引是按键值的顺序存储的，对于 IO 密集型的范围查找来说，对比随机从磁盘读取每一行的数据 IO 要少的多，因此利用覆盖索引在访问时也可以把磁盘的随机读取的 IO 转变成索引查找的顺序 IO。</li>
</ul>
<p><strong>索引 SET 规范</strong></p>
<p><strong>尽量避免使用外键约束</strong></p>
<ul>
<li>不建议使用外键约束（foreign key），但一定要在表与表之间的关联键上建立索引</li>
<li>外键可用于保证数据的参照完整性，但建议在业务端实现</li>
<li>外键会影响父表和子表的写操作从而降低性能</li>
</ul>
<hr>
<h2 id="8、数据库-SQL-开发规范"><a href="#8、数据库-SQL-开发规范" class="headerlink" title="8、数据库 SQL 开发规范"></a>8、数据库 SQL 开发规范</h2><h4 id="建议使用预编译语句进行数据库操作"><a href="#建议使用预编译语句进行数据库操作" class="headerlink" title="建议使用预编译语句进行数据库操作"></a>建议使用预编译语句进行数据库操作</h4><h4 id="避免数据类型的隐式转换"><a href="#避免数据类型的隐式转换" class="headerlink" title="避免数据类型的隐式转换"></a>避免数据类型的隐式转换</h4><h4 id="充分利用表上已经存在的索引"><a href="#充分利用表上已经存在的索引" class="headerlink" title="充分利用表上已经存在的索引"></a>充分利用表上已经存在的索引</h4><p>避免使用双%号的查询条件。如：<code>a like &#39;%123%&#39;</code>，（如果无前置%,只有后置%，是可以用到列上的索引的）</p>
<p>一个 SQL 只能利用到复合索引中的一列进行范围查询。如：有 a,b,c 列的联合索引，在查询条件中有 a 列的范围查询，则在 b,c 列上的索引将不会被用到。</p>
<p>在定义联合索引时，如果 a 列要用到范围查找的话，就要把 a 列放到联合索引的右侧，使用 left join 或 not exists 来优化 not in 操作，因为 not in 也通常会使用索引失效。</p>
<h4 id="程序连接不同的数据库使用不同的账号，禁止跨库查询"><a href="#程序连接不同的数据库使用不同的账号，禁止跨库查询" class="headerlink" title="程序连接不同的数据库使用不同的账号，禁止跨库查询"></a>程序连接不同的数据库使用不同的账号，禁止跨库查询</h4><ul>
<li>为数据库迁移和分库分表留出余地</li>
<li>降低业务耦合度</li>
<li>避免权限过大而产生的安全风险</li>
</ul>
<h4 id="禁止使用-SELECT-必须使用-SELECT-lt-字段列表-gt-查询"><a href="#禁止使用-SELECT-必须使用-SELECT-lt-字段列表-gt-查询" class="headerlink" title="禁止使用 SELECT * 必须使用 SELECT &lt;字段列表&gt; 查询"></a>禁止使用 SELECT * 必须使用 SELECT &lt;字段列表&gt; 查询</h4><p><strong>原因：</strong></p>
<ul>
<li>消耗更多的 CPU 和 IO 以网络带宽资源</li>
<li>无法使用覆盖索引</li>
<li>可减少表结构变更带来的影响</li>
</ul>
<h4 id="禁止使用不含字段列表的-INSERT-语句"><a href="#禁止使用不含字段列表的-INSERT-语句" class="headerlink" title="禁止使用不含字段列表的 INSERT 语句"></a>禁止使用不含字段列表的 INSERT 语句</h4><p>如：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">insert into values (&#39;a&#39;,&#39;b&#39;,&#39;c&#39;);</span><br></pre></td></tr></table></figure>
<p>应使用：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">insert into t(c1,c2,c3) values (&#39;a&#39;,&#39;b&#39;,&#39;c&#39;);</span><br></pre></td></tr></table></figure>


<h4 id="避免使用子查询，可以把子查询优化为-join-操作"><a href="#避免使用子查询，可以把子查询优化为-join-操作" class="headerlink" title="避免使用子查询，可以把子查询优化为 join 操作"></a>避免使用子查询，可以把子查询优化为 join 操作</h4><p>通常子查询在 in 子句中，且子查询中为简单 SQL(不包含 union、group by、order by、limit 从句) 时,才可以把子查询转化为关联查询进行优化。</p>
<p><strong>子查询性能差的原因：</strong></p>
<p>子查询的结果集无法使用索引，通常子查询的结果集会被存储到临时表中，不论是内存临时表还是磁盘临时表都不会存在索引，所以查询性能会受到一定的影响。特别是对于返回结果集比较大的子查询，其对查询性能的影响也就越大。</p>
<p><strong>由于子查询会产生大量的临时表也没有索引，所以会消耗过多的 CPU 和 IO 资源，产生大量的<u>慢查询</u>。</strong></p>
<h4 id="避免使用-JOIN-关联太多的表"><a href="#避免使用-JOIN-关联太多的表" class="headerlink" title="避免使用 JOIN 关联太多的表"></a>避免使用 JOIN 关联太多的表</h4><p>对于 MySQL 来说，是存在关联缓存的，缓存的大小可以由 join_buffer_size 参数进行设置。</p>
<p>在 MySQL 中，对于同一个 SQL 多关联（join）一个表，就会多分配一个关联缓存，如果在一个 SQL 中关联的表越多，所占用的内存也就越大。</p>
<p>如果程序中大量的使用了多表关联的操作，同时 join_buffer_size 设置的也不合理的情况下，就容易造成服务器内存溢出的情况，就会影响到服务器数据库性能的稳定性。</p>
<p>同时对于关联操作来说，会产生临时表操作，影响查询效率，MySQL 最多允许关联 61 个表，建议不超过 5 个。</p>
<h4 id="减少同数据库的交互次数"><a href="#减少同数据库的交互次数" class="headerlink" title="减少同数据库的交互次数"></a>减少同数据库的交互次数</h4><p>数据库更适合处理批量操作，合并多个相同的操作到一起，可以提高处理效率。</p>
<h4 id="对应同一列进行-or-判断时，使用-in-代替-or"><a href="#对应同一列进行-or-判断时，使用-in-代替-or" class="headerlink" title="对应同一列进行 or 判断时，使用 in 代替 or"></a>对应同一列进行 or 判断时，使用 in 代替 or</h4><p>in 的值不要超过 500 个，in 操作可以更有效的利用索引，or 大多数情况下很少能利用到索引。</p>
<h4 id="禁止使用-order-by-rand-进行随机排序"><a href="#禁止使用-order-by-rand-进行随机排序" class="headerlink" title="禁止使用 order by rand() 进行随机排序"></a>禁止使用 order by rand() 进行随机排序</h4><p>order by rand() 会把表中所有符合条件的数据装载到内存中，然后在内存中对所有数据根据随机生成的值进行排序，并且可能会对每一行都生成一个随机值，如果满足条件的数据集非常大，就会消耗大量的 CPU 和 IO 及内存资源。</p>
<h4 id="WHERE-从句中禁止对列进行函数转换和计算"><a href="#WHERE-从句中禁止对列进行函数转换和计算" class="headerlink" title="WHERE 从句中禁止对列进行函数转换和计算"></a>WHERE 从句中禁止对列进行函数转换和计算</h4><p><strong>对列进行函数转换或计算时会导致无法使用索引</strong></p>
<p><strong>不推荐：</strong></p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">where date(create_time)&#x3D;&#39;20190101&#39;</span><br></pre></td></tr></table></figure>
<p><strong>推荐：</strong></p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">where create_time &gt;&#x3D; &#39;20190101&#39; and create_time &lt; &#39;20190102&#39;</span><br></pre></td></tr></table></figure>
<h3 id=""><a href="#" class="headerlink" title=""></a></h3><h4 id="在明显不会有重复值时使用-UNION-ALL-而不是-UNION"><a href="#在明显不会有重复值时使用-UNION-ALL-而不是-UNION" class="headerlink" title="在明显不会有重复值时使用 UNION ALL 而不是 UNION"></a>在明显不会有重复值时使用 UNION ALL 而不是 UNION</h4><ul>
<li>UNION 会把两个结果集的所有数据放到临时表中后再进行去重操作</li>
<li>UNION ALL 不会再对结果集进行去重操作</li>
</ul>
<h4 id="拆分复杂的大-SQL-为多个小-SQL"><a href="#拆分复杂的大-SQL-为多个小-SQL" class="headerlink" title="拆分复杂的大 SQL 为多个小 SQL"></a>拆分复杂的大 SQL 为多个小 SQL</h4><ul>
<li>大 SQL 逻辑上比较复杂，需要占用大量 CPU 进行计算的 SQL</li>
<li>MySQL 中，一个 SQL 只能使用一个 CPU 进行计算</li>
<li>SQL 拆分后可以通过并行执行来提高处理效率</li>
</ul>
<hr>
<h2 id="9、数据库操作行为规范"><a href="#9、数据库操作行为规范" class="headerlink" title="9、数据库操作行为规范"></a>9、数据库操作行为规范</h2><h4 id="超-100-万行的批量写-UPDATE-DELETE-INSERT-操作-要分批多次进行操作"><a href="#超-100-万行的批量写-UPDATE-DELETE-INSERT-操作-要分批多次进行操作" class="headerlink" title="超 100 万行的批量写 (UPDATE,DELETE,INSERT) 操作,要分批多次进行操作"></a>超 100 万行的批量写 (UPDATE,DELETE,INSERT) 操作,要分批多次进行操作</h4><p><strong>大批量操作可能会造成严重的主从延迟</strong></p>
<p>主从环境中,大批量操作可能会造成严重的主从延迟，大批量的写操作一般都需要执行一定长的时间，<br>而只有当主库上执行完成后，才会在其他从库上执行，所以会造成主库与从库长时间的延迟情况</p>
<p><strong>binlog 日志为 row 格式时会产生大量的日志</strong></p>
<p>大批量写操作会产生大量日志，特别是对于 row 格式二进制数据而言，由于在 row 格式中会记录每一行数据的修改，我们一次修改的数据越多，产生的日志量也就会越多，日志的传输和恢复所需要的时间也就越长，这也是造成主从延迟的一个原因</p>
<p><strong>避免产生大事务操作</strong></p>
<p>大批量修改数据，一定是在一个事务中进行的，这就会造成表中大批量数据进行锁定，从而导致大量的阻塞，阻塞会对 MySQL 的性能产生非常大的影响。</p>
<p>特别是长时间的阻塞会<strong>占满所有数据库的可用连接</strong>，这会使生产环境中的其他应用无法连接到数据库，因此一定要注意<strong>大批量写操作要进行分批</strong></p>
<h3 id="对于大表使用-pt-online-schema-change-修改表结构"><a href="#对于大表使用-pt-online-schema-change-修改表结构" class="headerlink" title="对于大表使用 pt-online-schema-change 修改表结构"></a>对于大表使用 pt-online-schema-change 修改表结构</h3><ul>
<li>避免大表修改产生的主从延迟</li>
<li>避免在对表字段进行修改时进行锁表</li>
</ul>
<p>对大表数据结构的修改一定要谨慎，会造成严重的<strong>锁表</strong>操作，尤其是生产环境，是不能容忍的。</p>
<p>pt-online-schema-change 它会首先<strong>建立一个与原表结构相同的新表</strong>，并且<strong>在新表上进行表结构的修改</strong>，然后<strong>再把原表中的数据复制到新表</strong>中，并在原表中增加一些触发器。<strong>把原表中新增的数据也复制到新表中</strong>，<strong>在行所有数据复制完成之后，把新表命名成原表，并把原来的表删除掉</strong>。把原来一个 DDL 操作，分解成多个小的批次进行。</p>
<h4 id="禁止为程序使用的账号赋予-super-权限"><a href="#禁止为程序使用的账号赋予-super-权限" class="headerlink" title="禁止为程序使用的账号赋予 super 权限"></a>禁止为程序使用的账号赋予 super 权限</h4><ul>
<li>当达到最大连接数限制时，还运行 1 个有 super 权限的用户连接</li>
<li><code>super</code> 权限只能留给 DBA 处理问题的账号使用</li>
</ul>
<h3 id="对于程序连接数据库账号-遵循权限最小原则"><a href="#对于程序连接数据库账号-遵循权限最小原则" class="headerlink" title="对于程序连接数据库账号,遵循权限最小原则"></a>对于程序连接数据库账号,遵循权限最小原则</h3><ul>
<li>程序使用数据库账号只能在一个 DB 下使用，不准跨库</li>
<li>程序使用的账号原则上不准有 <code>drop</code> 权限</li>
</ul>
<hr>
<h2 id="10、索引补充"><a href="#10、索引补充" class="headerlink" title="10、索引补充"></a>10、索引补充</h2><h4 id="为什么要使用索引？"><a href="#为什么要使用索引？" class="headerlink" title="为什么要使用索引？"></a>为什么要使用索引？</h4><ol>
<li>通过创建唯一性索引，可以保证数据库表中每一行数据的唯一性。</li>
<li>可以大大加快 数据的检索速度（大大减少的检索的数据量）,  这也是创建索引的最主要的原因。 </li>
<li><strong>帮助服务器避免排序和临时表。</strong></li>
<li>将随机IO变为顺序IO</li>
<li>可以加速表和表之间的连接，特别是在实现数据的参考完整性方面特别有意义。</li>
</ol>
<h4 id="不对表中的每一个列创建一个索引"><a href="#不对表中的每一个列创建一个索引" class="headerlink" title="不对表中的每一个列创建一个索引"></a>不对表中的每一个列创建一个索引</h4><ol>
<li>当对表中的数据进行增加、删除和修改的时候，索引也要动态的维护，这样就降低了数据的维护速度。 </li>
<li>索引需要占物理空间，除了数据表占数据空间之外，每一个索引还要占一定的物理空间，如果要建立聚簇索引，那么需要的空间就会更大。 </li>
<li>创建索引和维护索引要耗费时间，这种时间随着数据量的增加而增加。 </li>
</ol>
<h4 id="注意事项"><a href="#注意事项" class="headerlink" title="注意事项"></a>注意事项</h4><ol>
<li><p>在经常需要搜索的列上，可以加快搜索的速度； </p>
</li>
<li><p>在经常使用在WHERE子句中的列上面创建索引，加快条件的判断速度。 </p>
</li>
<li><p>在经常需要排序的列上创 建索引，因为索引已经排序，这样查询可以利用索引的排序，加快排序查询时间； </p>
</li>
<li><p>对于中到大型表索引都是非常有效的，但是特大型表的话维护开销会很大，不适合建索引</p>
</li>
<li><p>在经常用在<strong>连接</strong>的列上，这 些列主要是一些<strong>外键</strong>，可以加快连接的速度； </p>
</li>
<li><p><strong>避免 where 子句中对字段施加函数</strong>，这会造成无法命中索引。</p>
</li>
<li><p>在使用InnoDB时使用与业务无关的自增主键作为主键，<strong>即使用逻辑主键，而不要使用业务主键</strong>。</p>
</li>
<li><p><del>将打算加索引的列设置为 NOT NULL ，否则将导致引擎放弃使用索引而进行全表扫描。</del> </p>
<p>订正。<strong>将某一列设置为default null，where 是可以走索引，另外索引列是否设置 null 是不影响性能的。</strong> 但是，还是不建议列上允许为空。最好限制not null，因为null需要更多的存储空间并且null值无法参与某些运算。</p>
<p>NULL 值的索引查找流程参考：<a target="_blank" rel="noopener" href="https://juejin.im/post/5d5defc2518825591523a1db">https://juejin.im/post/5d5defc2518825591523a1db</a> ,相关阅读：<a target="_blank" rel="noopener" href="https://juejin.im/post/5d5defc2518825591523a1db">MySQL中IS NULL、IS NOT NULL、!=不能用索引？胡扯！</a> 。</p>
</li>
<li><p>删除长期未使用的索引，不用的索引的存在会造成不必要的性能损耗 MySQL 5.7 可以通过查询 sys 库的 <code>chema_unused_indexes</code> 视图来查询哪些索引从未被使用</p>
</li>
<li><p>在使用 limit offset 查询缓慢时，可以借助索引来提高性能</p>
</li>
</ol>
<h3 id="覆盖索引使用实例"><a href="#覆盖索引使用实例" class="headerlink" title="覆盖索引使用实例"></a>覆盖索引使用实例</h3><p>现在创建了索引(username,age)，执行下面的 sql 语句</p>
<figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">select</span> username , age <span class="keyword">from</span> <span class="keyword">user</span> <span class="keyword">where</span> username <span class="operator">=</span> <span class="string">&#x27;Java&#x27;</span> <span class="keyword">and</span> age <span class="operator">=</span> <span class="number">22</span></span><br></pre></td></tr></table></figure>


<h4 id="MySQL-的基本存储结构"><a href="#MySQL-的基本存储结构" class="headerlink" title="MySQL 的基本存储结构"></a>MySQL 的基本存储结构</h4><p><strong>MySQL的基本存储结构是页(记录都存在页里边)</strong></p>
<ul>
<li><strong>各个数据页可以组成一个双向链表</strong></li>
<li><strong>每个数据页中的记录又可以组成一个单向链表</strong><ul>
<li>每个数据页都会为存储在它里边儿的记录生成一个页目录，在通过主键查找某条记录的时候可以在页目录中使用<strong>二分法</strong>快速定位到对应的槽，然后再遍历该槽对应分组中的记录即可快速找到指定的记录</li>
<li>以其他列(非主键)作为搜索条件：只能从最小记录开始依次遍历单链表中的每条记录。</li>
</ul>
</li>
</ul>
<p>所以说，如果我们写select * from user where indexname = ‘xxx’这样没有进行任何优化的sql语句，默认会这样做：</p>
<ol>
<li><strong>定位到记录所在的页：需要遍历双向链表，找到所在的页</strong></li>
<li><strong>从所在的页内中查找相应的记录：由于不是根据主键查询，只能遍历所在页的单链表了</strong></li>
</ol>
<p>很明显，在数据量很大的情况下这样查找会很慢！这样的时间复杂度为O（n）。</p>
<h4 id="最左前缀原则"><a href="#最左前缀原则" class="headerlink" title="最左前缀原则"></a>最左前缀原则</h4><p>MySQL中的索引可以以一定顺序引用多列，这种索引叫作联合索引。如User表的name和city加联合索引就是(name,city)，而最左前缀原则指的是，如果查询的时候查询条件精确匹配索引的左边连续一列或几列，则此列就可以被用到。如下：        </p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">select * from user where name&#x3D;xx and city&#x3D;xx ; ／／可以命中索引</span><br><span class="line">select * from user where name&#x3D;xx ; &#x2F;&#x2F; 可以命中索引</span><br><span class="line">select * from user where city&#x3D;xx ; &#x2F;&#x2F; 无法命中索引            </span><br></pre></td></tr></table></figure>
<p>这里需要注意的是，查询的时候如果两个条件都用上了，但是顺序不同，如 <code>city= xx and name ＝xx</code>，那么现在的查询引擎会自动优化为匹配联合索引的顺序，这样是能够命中索引的。</p>
<p>由于最左前缀原则，在创建联合索引时，<strong>索引字段的顺序需要考虑字段值去重之后的个数，较多的放前面。ORDER BY子句也遵循此规则</strong>。</p>
<h4 id="注意避免冗余索引"><a href="#注意避免冗余索引" class="headerlink" title="注意避免冗余索引"></a>注意避免冗余索引</h4><p>冗余索引指的是索引的功能相同，能够命中 就肯定能命中 ，那么就是冗余索引如（name,city ）和（name ）这两个索引就是冗余索引，在大多数情况下，都应该尽量扩展已有的索引而不是创建新索引。</p>
<p>MySQL 5.7 版本后，可以通过查询 sys 库的 <code>schema_redundant_indexes</code> 表来查看冗余索引  </p>
<h3 id="Mysql如何为表字段添加索引？？？"><a href="#Mysql如何为表字段添加索引？？？" class="headerlink" title="Mysql如何为表字段添加索引？？？"></a>Mysql如何为表字段添加索引？？？</h3><p>1.添加PRIMARY KEY（主键索引）</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">ALTER TABLE &#96;table_name&#96; ADD PRIMARY KEY ( &#96;column&#96; ) </span><br></pre></td></tr></table></figure>
<p>2.添加UNIQUE(唯一索引) </p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">ALTER TABLE &#96;table_name&#96; ADD UNIQUE ( &#96;column&#96; ) </span><br></pre></td></tr></table></figure>
<p>3.添加INDEX(普通索引) </p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">ALTER TABLE &#96;table_name&#96; ADD INDEX index_name ( &#96;column&#96; )</span><br></pre></td></tr></table></figure>
<p>4.添加FULLTEXT(全文索引) </p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">ALTER TABLE &#96;table_name&#96; ADD FULLTEXT ( &#96;column&#96;) </span><br></pre></td></tr></table></figure>
<p>5.添加多列索引</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">ALTER TABLE &#96;table_name&#96; ADD INDEX index_name ( &#96;column1&#96;, &#96;column2&#96;, &#96;column3&#96; )</span><br></pre></td></tr></table></figure>
<hr>
<h2 id="11、模糊查询"><a href="#11、模糊查询" class="headerlink" title="11、模糊查询"></a>11、模糊查询</h2><p>对于模糊查询阿里巴巴开发手册这样说到：</p>
<blockquote>
<p>【强制】页面搜索严禁左模糊或者全模糊，如果需要请走搜索引擎来解决。</p>
<p>说明:索引文件具有 B-Tree 的最左前缀匹配特性，如果左边的值未确定，那么无法使用此索引。</p>
</blockquote>
<hr>
<h2 id="12、外键和级联"><a href="#12、外键和级联" class="headerlink" title="12、外键和级联"></a>12、外键和级联</h2><p>对于外键和级联，阿里巴巴开发手册这样说到：</p>
<blockquote>
<p>【强制】不得使用外键与级联，一切外键概念必须在应用层解决。</p>
<p>说明:以学生和成绩的关系为例，学生表中的 student_id 是主键，那么成绩表中的 student_id 则为外键。如果更新学生表中的 student_id，同时触发成绩表中的 student_id 更新，即为<strong>级联更新</strong>。<strong>外键与级联更新适用于单机低并发，不适合分布式、高并发集群</strong>;级联更新是强阻塞，存在数据库更新风暴的风险; 外键影响数据库的插入速度</p>
</blockquote>
<p>为什么不要用外键呢？大部分人可能会这样回答：</p>
<blockquote>
<ol>
<li><strong>增加了复杂性：</strong> a.每次做DELETE 或者UPDATE都必须考虑外键约束，会导致开发的时候很痛苦,测试数据极为不方便;b.外键的主从关系是定的，假如那天需求有变化，数据库中的这个字段根本不需要和其他表有关联的话就会增加很多麻烦。</li>
<li><strong>增加了额外工作</strong>： 数据库需要增加维护外键的工作，比如当我们做一些涉及外键字段的增，删，更新操作之后，需要触发相关操作去检查，保证数据的的一致性和正确性，这样会不得不消耗资源；（个人觉得这个不是不用外键的原因，因为即使你不使用外键，你在应用层面也还是要保证的。所以，我觉得这个影响可以忽略不计。）</li>
<li>外键还会因为需要请求对其他表内部加锁而容易出现死锁情况；</li>
<li><strong>对分库分表不友好</strong> ：因为分库分表下外键是无法生效的。</li>
<li>……</li>
</ol>
</blockquote>
<p>上面这种回答不是特别的全面，只是说了外键存在的一个常见的问题。实际上，外键也是有很多好处的，比如：</p>
<ol>
<li>保证了数据库数据的一致性和完整性；</li>
<li>级联操作方便，减轻了程序代码量；</li>
<li>……</li>
</ol>
<p>所以说，不要一股脑的就抛弃了外键这个概念，既然它存在就有它存在的道理，如果系统不涉及分不分表，并发量不是很高的情况还是可以考虑使用外键的。</p>
<hr>
<h2 id="13、关于-Transactional注解"><a href="#13、关于-Transactional注解" class="headerlink" title="13、关于@Transactional注解"></a>13、关于@Transactional注解</h2><p>对于<code>@Transactional</code>事务注解，阿里巴巴开发手册这样说到：</p>
<blockquote>
<p>【参考】@Transactional事务不要滥用。事务会影响数据库的QPS，另外使用事务的地方需要考虑各方面的回滚方案，包括缓存回滚、搜索引擎回滚、消息补偿、统计修正等。</p>
</blockquote>
</article><div class="post-copyright"><div class="post-copyright__author"><span class="post-copyright-meta">文章作者: </span><span class="post-copyright-info"><a href="mailto:undefined">蔡哞哞</a></span></div><div class="post-copyright__type"><span class="post-copyright-meta">文章链接: </span><span class="post-copyright-info"><a href="https://caixm1025.gitee.io/2021/03/04/03-04%E6%95%B0%E6%8D%AE%E5%BA%93%E5%AD%A6%E4%B9%A0/">https://caixm1025.gitee.io/2021/03/04/03-04%E6%95%B0%E6%8D%AE%E5%BA%93%E5%AD%A6%E4%B9%A0/</a></span></div><div class="post-copyright__notice"><span class="post-copyright-meta">版权声明: </span><span class="post-copyright-info">本博客所有文章除特别声明外，均采用 <a href="https://creativecommons.org/licenses/by-nc-sa/4.0/" target="_blank">CC BY-NC-SA 4.0</a> 许可协议。转载请注明来自 <a href="https://caixm1025.gitee.io" target="_blank">个人博客</a>！</span></div></div><div class="tag_share"><div class="post-meta__tag-list"><a class="post-meta__tags" href="/everyday/tags/MySQL/">MySQL</a></div><div class="post_share"><div class="social-share" data-image="/everyday/img/%E5%9B%BE%E7%89%874.jpg" data-sites="facebook,twitter,wechat,weibo,qq"></div><link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/social-share.js/dist/css/share.min.css" media="print" onload="this.media='all'"><script src="https://cdn.jsdelivr.net/npm/social-share.js/dist/js/social-share.min.js" defer></script></div></div><nav class="pagination-post" id="pagination"><div class="prev-post pull-left"><a href="/everyday/2021/03/06/03-05%E6%95%B0%E6%8D%AE%E5%BA%93%E5%AD%A6%E4%B9%A0/"><img class="prev-cover" src="/everyday/img/%E5%9B%BE%E7%89%875.jpg" onerror="onerror=null;src='/everyday/img/404.jpg'" alt="cover of previous post"><div class="pagination-info"><div class="label">上一篇</div><div class="prev_info">03-05数据库学习</div></div></a></div><div class="next-post pull-right"><a href="/everyday/2021/03/03/03-03%E9%9D%A2%E8%AF%95%E9%A2%98%E5%AD%A6%E4%B9%A0/"><img class="next-cover" src="/everyday/img/%E5%9B%BE%E7%89%873.jpg" onerror="onerror=null;src='/everyday/img/404.jpg'" alt="cover of next post"><div class="pagination-info"><div class="label">下一篇</div><div class="next_info">03-03面试题学习</div></div></a></div></nav><div class="relatedPosts"><div class="headline"><i class="fas fa-thumbs-up fa-fw"></i><span> 相关推荐</span></div><div class="relatedPosts-list"><div><a href="/everyday/2021/02/26/02-26数据库学习/" title="02-26数据库学习"><img class="cover" src="/everyday/img/%E5%9B%BE%E7%89%8726.jpg" alt="cover"><div class="content is-center"><div class="date"><i class="far fa-calendar-alt fa-fw"></i> 2021-02-26</div><div class="title">02-26数据库学习</div></div></a></div><div><a href="/everyday/2021/03/06/03-05数据库学习/" title="03-05数据库学习"><img class="cover" src="/everyday/img/%E5%9B%BE%E7%89%875.jpg" alt="cover"><div class="content is-center"><div class="date"><i class="far fa-calendar-alt fa-fw"></i> 2021-03-06</div><div class="title">03-05数据库学习</div></div></a></div></div></div></div><div class="aside-content" id="aside-content"><div class="sticky_layout"><div class="card-widget" id="card-toc"><div class="item-headline"><i class="fas fa-stream"></i><span>目录</span></div><div class="toc-content"><ol class="toc"><li class="toc-item toc-level-1"><a class="toc-link" href="#%E5%85%A8%E6%96%87%E7%BB%9D%E5%A4%A7%E9%83%A8%E5%88%86%E5%87%BA%E5%A4%84%EF%BC%9Ahttps-gitee-com-SnailClimb-JavaGuide"><span class="toc-number">1.</span> <span class="toc-text">全文绝大部分出处：https:&#x2F;&#x2F;gitee.com&#x2F;SnailClimb&#x2F;JavaGuide</span></a><ol class="toc-child"><li class="toc-item toc-level-2"><a class="toc-link" href="#1%E3%80%81%E7%B4%A2%E5%BC%95"><span class="toc-number">1.1.</span> <span class="toc-text">1、索引</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#2%E3%80%81%E6%9F%A5%E8%AF%A2%E7%BC%93%E5%AD%98%E7%9A%84%E4%BD%BF%E7%94%A8"><span class="toc-number">1.2.</span> <span class="toc-text">2、查询缓存的使用</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#3%E3%80%81%E4%BA%8B%E5%8A%A1"><span class="toc-number">1.3.</span> <span class="toc-text">3、事务</span></a><ol class="toc-child"><li class="toc-item toc-level-4"><a class="toc-link" href="#%E4%BA%8B%E5%8A%A1%E7%9A%84%E5%9B%9B%E5%A4%A7%E7%89%B9%E6%80%A7-ACID"><span class="toc-number">1.3.0.1.</span> <span class="toc-text">事务的四大特性(ACID)</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E5%B9%B6%E5%8F%91%E4%BA%8B%E5%8A%A1%E5%B8%A6%E6%9D%A5%E5%93%AA%E4%BA%9B%E9%97%AE%E9%A2%98"><span class="toc-number">1.3.0.2.</span> <span class="toc-text">并发事务带来哪些问题?</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E4%BA%8B%E5%8A%A1%E9%9A%94%E7%A6%BB%E7%BA%A7%E5%88%AB%E6%9C%89%E5%93%AA%E4%BA%9B-MySQL%E7%9A%84%E9%BB%98%E8%AE%A4%E9%9A%94%E7%A6%BB%E7%BA%A7%E5%88%AB%E6%98%AF"><span class="toc-number">1.3.0.3.</span> <span class="toc-text">事务隔离级别有哪些?MySQL的默认隔离级别是?</span></a></li></ol></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#4%E3%80%81%E9%94%81%E6%9C%BA%E5%88%B6%E4%B8%8EInnoDB%E9%94%81%E7%AE%97%E6%B3%95"><span class="toc-number">1.4.</span> <span class="toc-text">4、锁机制与InnoDB锁算法</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#5%E3%80%81%E5%A4%A7%E8%A1%A8%E4%BC%98%E5%8C%96"><span class="toc-number">1.5.</span> <span class="toc-text">5、大表优化</span></a><ol class="toc-child"><li class="toc-item toc-level-4"><a class="toc-link" href="#1-%E9%99%90%E5%AE%9A%E6%95%B0%E6%8D%AE%E7%9A%84%E8%8C%83%E5%9B%B4"><span class="toc-number">1.5.0.1.</span> <span class="toc-text">1. 限定数据的范围</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#2-%E8%AF%BB-%E5%86%99%E5%88%86%E7%A6%BB"><span class="toc-number">1.5.0.2.</span> <span class="toc-text">2. 读&#x2F;写分离</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#3-%E5%9E%82%E7%9B%B4%E5%88%86%E5%8C%BA"><span class="toc-number">1.5.0.3.</span> <span class="toc-text">3. 垂直分区</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#4-%E6%B0%B4%E5%B9%B3%E5%88%86%E5%8C%BA"><span class="toc-number">1.5.0.4.</span> <span class="toc-text">4. 水平分区</span></a></li></ol></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E8%A7%A3%E9%87%8A%E4%B8%80%E4%B8%8B%E4%BB%80%E4%B9%88%E6%98%AF%E6%B1%A0%E5%8C%96%E8%AE%BE%E8%AE%A1%E6%80%9D%E6%83%B3%E3%80%82%E4%BB%80%E4%B9%88%E6%98%AF%E6%95%B0%E6%8D%AE%E5%BA%93%E8%BF%9E%E6%8E%A5%E6%B1%A0-%E4%B8%BA%E4%BB%80%E4%B9%88%E9%9C%80%E8%A6%81%E6%95%B0%E6%8D%AE%E5%BA%93%E8%BF%9E%E6%8E%A5%E6%B1%A0"><span class="toc-number">1.5.1.</span> <span class="toc-text">解释一下什么是池化设计思想。什么是数据库连接池?为什么需要数据库连接池?</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E5%88%86%E5%BA%93%E5%88%86%E8%A1%A8%E4%B9%8B%E5%90%8E-id-%E4%B8%BB%E9%94%AE%E5%A6%82%E4%BD%95%E5%A4%84%E7%90%86%EF%BC%9F"><span class="toc-number">1.5.2.</span> <span class="toc-text">分库分表之后,id 主键如何处理？</span></a><ol class="toc-child"><li class="toc-item toc-level-4"><a class="toc-link" href="#%E5%B0%BD%E9%87%8F%E5%81%9A%E5%88%B0%E5%86%B7%E7%83%AD%E6%95%B0%E6%8D%AE%E5%88%86%E7%A6%BB-%E5%87%8F%E5%B0%8F%E8%A1%A8%E7%9A%84%E5%AE%BD%E5%BA%A6"><span class="toc-number">1.5.2.1.</span> <span class="toc-text">尽量做到冷热数据分离,减小表的宽度</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E7%A6%81%E6%AD%A2%E5%9C%A8%E8%A1%A8%E4%B8%AD%E5%BB%BA%E7%AB%8B%E9%A2%84%E7%95%99%E5%AD%97%E6%AE%B5"><span class="toc-number">1.5.2.2.</span> <span class="toc-text">禁止在表中建立预留字段</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E7%A6%81%E6%AD%A2%E5%9C%A8%E6%95%B0%E6%8D%AE%E5%BA%93%E4%B8%AD%E5%AD%98%E5%82%A8%E5%9B%BE%E7%89%87-%E6%96%87%E4%BB%B6%E7%AD%89%E5%A4%A7%E7%9A%84%E4%BA%8C%E8%BF%9B%E5%88%B6%E6%95%B0%E6%8D%AE"><span class="toc-number">1.5.2.3.</span> <span class="toc-text">禁止在数据库中存储图片,文件等大的二进制数据</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E7%A6%81%E6%AD%A2%E5%9C%A8%E7%BA%BF%E4%B8%8A%E5%81%9A%E6%95%B0%E6%8D%AE%E5%BA%93%E5%8E%8B%E5%8A%9B%E6%B5%8B%E8%AF%95"><span class="toc-number">1.5.2.4.</span> <span class="toc-text">禁止在线上做数据库压力测试</span></a></li></ol></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#6%E3%80%81%E6%95%B0%E6%8D%AE%E5%BA%93%E5%AD%97%E6%AE%B5%E8%AE%BE%E8%AE%A1%E8%A7%84%E8%8C%83"><span class="toc-number">1.6.</span> <span class="toc-text">6、数据库字段设计规范</span></a><ol class="toc-child"><li class="toc-item toc-level-4"><a class="toc-link" href="#%E4%BC%98%E5%85%88%E9%80%89%E6%8B%A9%E7%AC%A6%E5%90%88%E5%AD%98%E5%82%A8%E9%9C%80%E8%A6%81%E7%9A%84%E6%9C%80%E5%B0%8F%E7%9A%84%E6%95%B0%E6%8D%AE%E7%B1%BB%E5%9E%8B"><span class="toc-number">1.6.0.1.</span> <span class="toc-text">优先选择符合存储需要的最小的数据类型</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E9%81%BF%E5%85%8D%E4%BD%BF%E7%94%A8-TEXT-BLOB-%E6%95%B0%E6%8D%AE%E7%B1%BB%E5%9E%8B%EF%BC%8C%E6%9C%80%E5%B8%B8%E8%A7%81%E7%9A%84-TEXT-%E7%B1%BB%E5%9E%8B%E5%8F%AF%E4%BB%A5%E5%AD%98%E5%82%A8-64k-%E7%9A%84%E6%95%B0%E6%8D%AE"><span class="toc-number">1.6.0.2.</span> <span class="toc-text">避免使用 TEXT,BLOB 数据类型，最常见的 TEXT 类型可以存储 64k 的数据</span></a></li></ol></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#7%E3%80%81%E7%B4%A2%E5%BC%95%E8%AE%BE%E8%AE%A1%E8%A7%84%E8%8C%83"><span class="toc-number">1.7.</span> <span class="toc-text">7、索引设计规范</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#8%E3%80%81%E6%95%B0%E6%8D%AE%E5%BA%93-SQL-%E5%BC%80%E5%8F%91%E8%A7%84%E8%8C%83"><span class="toc-number">1.8.</span> <span class="toc-text">8、数据库 SQL 开发规范</span></a><ol class="toc-child"><li class="toc-item toc-level-4"><a class="toc-link" href="#%E5%BB%BA%E8%AE%AE%E4%BD%BF%E7%94%A8%E9%A2%84%E7%BC%96%E8%AF%91%E8%AF%AD%E5%8F%A5%E8%BF%9B%E8%A1%8C%E6%95%B0%E6%8D%AE%E5%BA%93%E6%93%8D%E4%BD%9C"><span class="toc-number">1.8.0.1.</span> <span class="toc-text">建议使用预编译语句进行数据库操作</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E9%81%BF%E5%85%8D%E6%95%B0%E6%8D%AE%E7%B1%BB%E5%9E%8B%E7%9A%84%E9%9A%90%E5%BC%8F%E8%BD%AC%E6%8D%A2"><span class="toc-number">1.8.0.2.</span> <span class="toc-text">避免数据类型的隐式转换</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E5%85%85%E5%88%86%E5%88%A9%E7%94%A8%E8%A1%A8%E4%B8%8A%E5%B7%B2%E7%BB%8F%E5%AD%98%E5%9C%A8%E7%9A%84%E7%B4%A2%E5%BC%95"><span class="toc-number">1.8.0.3.</span> <span class="toc-text">充分利用表上已经存在的索引</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E7%A8%8B%E5%BA%8F%E8%BF%9E%E6%8E%A5%E4%B8%8D%E5%90%8C%E7%9A%84%E6%95%B0%E6%8D%AE%E5%BA%93%E4%BD%BF%E7%94%A8%E4%B8%8D%E5%90%8C%E7%9A%84%E8%B4%A6%E5%8F%B7%EF%BC%8C%E7%A6%81%E6%AD%A2%E8%B7%A8%E5%BA%93%E6%9F%A5%E8%AF%A2"><span class="toc-number">1.8.0.4.</span> <span class="toc-text">程序连接不同的数据库使用不同的账号，禁止跨库查询</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E7%A6%81%E6%AD%A2%E4%BD%BF%E7%94%A8-SELECT-%E5%BF%85%E9%A1%BB%E4%BD%BF%E7%94%A8-SELECT-lt-%E5%AD%97%E6%AE%B5%E5%88%97%E8%A1%A8-gt-%E6%9F%A5%E8%AF%A2"><span class="toc-number">1.8.0.5.</span> <span class="toc-text">禁止使用 SELECT * 必须使用 SELECT &lt;字段列表&gt; 查询</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E7%A6%81%E6%AD%A2%E4%BD%BF%E7%94%A8%E4%B8%8D%E5%90%AB%E5%AD%97%E6%AE%B5%E5%88%97%E8%A1%A8%E7%9A%84-INSERT-%E8%AF%AD%E5%8F%A5"><span class="toc-number">1.8.0.6.</span> <span class="toc-text">禁止使用不含字段列表的 INSERT 语句</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E9%81%BF%E5%85%8D%E4%BD%BF%E7%94%A8%E5%AD%90%E6%9F%A5%E8%AF%A2%EF%BC%8C%E5%8F%AF%E4%BB%A5%E6%8A%8A%E5%AD%90%E6%9F%A5%E8%AF%A2%E4%BC%98%E5%8C%96%E4%B8%BA-join-%E6%93%8D%E4%BD%9C"><span class="toc-number">1.8.0.7.</span> <span class="toc-text">避免使用子查询，可以把子查询优化为 join 操作</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E9%81%BF%E5%85%8D%E4%BD%BF%E7%94%A8-JOIN-%E5%85%B3%E8%81%94%E5%A4%AA%E5%A4%9A%E7%9A%84%E8%A1%A8"><span class="toc-number">1.8.0.8.</span> <span class="toc-text">避免使用 JOIN 关联太多的表</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E5%87%8F%E5%B0%91%E5%90%8C%E6%95%B0%E6%8D%AE%E5%BA%93%E7%9A%84%E4%BA%A4%E4%BA%92%E6%AC%A1%E6%95%B0"><span class="toc-number">1.8.0.9.</span> <span class="toc-text">减少同数据库的交互次数</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E5%AF%B9%E5%BA%94%E5%90%8C%E4%B8%80%E5%88%97%E8%BF%9B%E8%A1%8C-or-%E5%88%A4%E6%96%AD%E6%97%B6%EF%BC%8C%E4%BD%BF%E7%94%A8-in-%E4%BB%A3%E6%9B%BF-or"><span class="toc-number">1.8.0.10.</span> <span class="toc-text">对应同一列进行 or 判断时，使用 in 代替 or</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E7%A6%81%E6%AD%A2%E4%BD%BF%E7%94%A8-order-by-rand-%E8%BF%9B%E8%A1%8C%E9%9A%8F%E6%9C%BA%E6%8E%92%E5%BA%8F"><span class="toc-number">1.8.0.11.</span> <span class="toc-text">禁止使用 order by rand() 进行随机排序</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#WHERE-%E4%BB%8E%E5%8F%A5%E4%B8%AD%E7%A6%81%E6%AD%A2%E5%AF%B9%E5%88%97%E8%BF%9B%E8%A1%8C%E5%87%BD%E6%95%B0%E8%BD%AC%E6%8D%A2%E5%92%8C%E8%AE%A1%E7%AE%97"><span class="toc-number">1.8.0.12.</span> <span class="toc-text">WHERE 从句中禁止对列进行函数转换和计算</span></a></li></ol></li><li class="toc-item toc-level-3"><a class="toc-link"><span class="toc-number">1.8.1.</span> <span class="toc-text"></span></a><ol class="toc-child"><li class="toc-item toc-level-4"><a class="toc-link" href="#%E5%9C%A8%E6%98%8E%E6%98%BE%E4%B8%8D%E4%BC%9A%E6%9C%89%E9%87%8D%E5%A4%8D%E5%80%BC%E6%97%B6%E4%BD%BF%E7%94%A8-UNION-ALL-%E8%80%8C%E4%B8%8D%E6%98%AF-UNION"><span class="toc-number">1.8.1.1.</span> <span class="toc-text">在明显不会有重复值时使用 UNION ALL 而不是 UNION</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E6%8B%86%E5%88%86%E5%A4%8D%E6%9D%82%E7%9A%84%E5%A4%A7-SQL-%E4%B8%BA%E5%A4%9A%E4%B8%AA%E5%B0%8F-SQL"><span class="toc-number">1.8.1.2.</span> <span class="toc-text">拆分复杂的大 SQL 为多个小 SQL</span></a></li></ol></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#9%E3%80%81%E6%95%B0%E6%8D%AE%E5%BA%93%E6%93%8D%E4%BD%9C%E8%A1%8C%E4%B8%BA%E8%A7%84%E8%8C%83"><span class="toc-number">1.9.</span> <span class="toc-text">9、数据库操作行为规范</span></a><ol class="toc-child"><li class="toc-item toc-level-4"><a class="toc-link" href="#%E8%B6%85-100-%E4%B8%87%E8%A1%8C%E7%9A%84%E6%89%B9%E9%87%8F%E5%86%99-UPDATE-DELETE-INSERT-%E6%93%8D%E4%BD%9C-%E8%A6%81%E5%88%86%E6%89%B9%E5%A4%9A%E6%AC%A1%E8%BF%9B%E8%A1%8C%E6%93%8D%E4%BD%9C"><span class="toc-number">1.9.0.1.</span> <span class="toc-text">超 100 万行的批量写 (UPDATE,DELETE,INSERT) 操作,要分批多次进行操作</span></a></li></ol></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E5%AF%B9%E4%BA%8E%E5%A4%A7%E8%A1%A8%E4%BD%BF%E7%94%A8-pt-online-schema-change-%E4%BF%AE%E6%94%B9%E8%A1%A8%E7%BB%93%E6%9E%84"><span class="toc-number">1.9.1.</span> <span class="toc-text">对于大表使用 pt-online-schema-change 修改表结构</span></a><ol class="toc-child"><li class="toc-item toc-level-4"><a class="toc-link" href="#%E7%A6%81%E6%AD%A2%E4%B8%BA%E7%A8%8B%E5%BA%8F%E4%BD%BF%E7%94%A8%E7%9A%84%E8%B4%A6%E5%8F%B7%E8%B5%8B%E4%BA%88-super-%E6%9D%83%E9%99%90"><span class="toc-number">1.9.1.1.</span> <span class="toc-text">禁止为程序使用的账号赋予 super 权限</span></a></li></ol></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E5%AF%B9%E4%BA%8E%E7%A8%8B%E5%BA%8F%E8%BF%9E%E6%8E%A5%E6%95%B0%E6%8D%AE%E5%BA%93%E8%B4%A6%E5%8F%B7-%E9%81%B5%E5%BE%AA%E6%9D%83%E9%99%90%E6%9C%80%E5%B0%8F%E5%8E%9F%E5%88%99"><span class="toc-number">1.9.2.</span> <span class="toc-text">对于程序连接数据库账号,遵循权限最小原则</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#10%E3%80%81%E7%B4%A2%E5%BC%95%E8%A1%A5%E5%85%85"><span class="toc-number">1.10.</span> <span class="toc-text">10、索引补充</span></a><ol class="toc-child"><li class="toc-item toc-level-4"><a class="toc-link" href="#%E4%B8%BA%E4%BB%80%E4%B9%88%E8%A6%81%E4%BD%BF%E7%94%A8%E7%B4%A2%E5%BC%95%EF%BC%9F"><span class="toc-number">1.10.0.1.</span> <span class="toc-text">为什么要使用索引？</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E4%B8%8D%E5%AF%B9%E8%A1%A8%E4%B8%AD%E7%9A%84%E6%AF%8F%E4%B8%80%E4%B8%AA%E5%88%97%E5%88%9B%E5%BB%BA%E4%B8%80%E4%B8%AA%E7%B4%A2%E5%BC%95"><span class="toc-number">1.10.0.2.</span> <span class="toc-text">不对表中的每一个列创建一个索引</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E6%B3%A8%E6%84%8F%E4%BA%8B%E9%A1%B9"><span class="toc-number">1.10.0.3.</span> <span class="toc-text">注意事项</span></a></li></ol></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E8%A6%86%E7%9B%96%E7%B4%A2%E5%BC%95%E4%BD%BF%E7%94%A8%E5%AE%9E%E4%BE%8B"><span class="toc-number">1.10.1.</span> <span class="toc-text">覆盖索引使用实例</span></a><ol class="toc-child"><li class="toc-item toc-level-4"><a class="toc-link" href="#MySQL-%E7%9A%84%E5%9F%BA%E6%9C%AC%E5%AD%98%E5%82%A8%E7%BB%93%E6%9E%84"><span class="toc-number">1.10.1.1.</span> <span class="toc-text">MySQL 的基本存储结构</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E6%9C%80%E5%B7%A6%E5%89%8D%E7%BC%80%E5%8E%9F%E5%88%99"><span class="toc-number">1.10.1.2.</span> <span class="toc-text">最左前缀原则</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E6%B3%A8%E6%84%8F%E9%81%BF%E5%85%8D%E5%86%97%E4%BD%99%E7%B4%A2%E5%BC%95"><span class="toc-number">1.10.1.3.</span> <span class="toc-text">注意避免冗余索引</span></a></li></ol></li><li class="toc-item toc-level-3"><a class="toc-link" href="#Mysql%E5%A6%82%E4%BD%95%E4%B8%BA%E8%A1%A8%E5%AD%97%E6%AE%B5%E6%B7%BB%E5%8A%A0%E7%B4%A2%E5%BC%95%EF%BC%9F%EF%BC%9F%EF%BC%9F"><span class="toc-number">1.10.2.</span> <span class="toc-text">Mysql如何为表字段添加索引？？？</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#11%E3%80%81%E6%A8%A1%E7%B3%8A%E6%9F%A5%E8%AF%A2"><span class="toc-number">1.11.</span> <span class="toc-text">11、模糊查询</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#12%E3%80%81%E5%A4%96%E9%94%AE%E5%92%8C%E7%BA%A7%E8%81%94"><span class="toc-number">1.12.</span> <span class="toc-text">12、外键和级联</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#13%E3%80%81%E5%85%B3%E4%BA%8E-Transactional%E6%B3%A8%E8%A7%A3"><span class="toc-number">1.13.</span> <span class="toc-text">13、关于@Transactional注解</span></a></li></ol></li></ol></div></div></div></div></main><footer id="footer" style="background-image: url(/everyday/img/%E5%9B%BE%E7%89%874.jpg)"><div id="footer-wrap"><div class="copyright">&copy;2020 - 2021 By 蔡哞哞</div><div class="framework-info"><span>框架 </span><a target="_blank" rel="noopener" href="https://hexo.io">Hexo</a><span class="footer-separator">|</span><span>主题 </span><a target="_blank" rel="noopener" href="https://github.com/jerryc127/hexo-theme-butterfly">Butterfly</a></div><div class="footer_custom_text">好好学习，<a  target="_blank" rel="noopener" href="https://butterfly.js.org/">天天向上</a>!</div></div></footer></div><div id="rightside"><div id="rightside-config-hide"><button id="readmode" type="button" title="阅读模式"><i class="fas fa-book-open"></i></button><button id="darkmode" type="button" title="浅色和深色模式转换"><i class="fas fa-adjust"></i></button><button id="hide-aside-btn" type="button" title="单栏和双栏切换"><i class="fas fa-arrows-alt-h"></i></button></div><div id="rightside-config-show"><button id="rightside_config" type="button" title="设置"><i class="fas fa-cog fa-spin"></i></button><button class="close" id="mobile-toc-button" type="button" title="目录"><i class="fas fa-list-ul"></i></button><button id="go-up" type="button" title="回到顶部"><i class="fas fa-arrow-up"></i></button></div></div><div><script src="/everyday/js/utils.js"></script><script src="/everyday/js/main.js"></script><div class="js-pjax"><script async src="//busuanzi.ibruce.info/busuanzi/2.3/busuanzi.pure.mini.js"></script></div><script defer="defer" id="fluttering_ribbon" mobile="true" src="https://cdn.jsdelivr.net/npm/butterfly-extsrc@1/dist/canvas-fluttering-ribbon.min.js"></script><script id="click-heart" src="https://cdn.jsdelivr.net/npm/butterfly-extsrc@1/dist/click-heart.min.js" async="async" mobile="false"></script></div></body></html>